package com.heima.wemedia.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSON;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.nacos.api.config.filter.IFilterConfig;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.common.constants.message.ArticleForEsConstants;
import com.heima.common.constants.message.NewsAutoScanConstants;
import com.heima.common.constants.message.WmNewsMessageConstants;
import com.heima.common.constants.wemedia.WemediaConstants;
import com.heima.common.exception.CustomException;
import com.heima.model.common.dtos.PageResponseResult;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.threadlocal.WmThreadLocalUtils;
import com.heima.model.wemedia.dto.NewsAuthDto;
import com.heima.model.wemedia.dto.WmNewsDto;
import com.heima.model.wemedia.dto.WmNewsPageReqDto;
import com.heima.model.wemedia.pojo.WmMaterial;
import com.heima.model.wemedia.pojo.WmNews;
import com.heima.model.wemedia.pojo.WmNewsMaterial;
import com.heima.model.wemedia.pojo.WmUser;
import com.heima.model.wemedia.vo.WmNewsVo;
import com.heima.wemedia.mapper.WmMaterialMapper;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.mapper.WmNewsMaterialMapper;
import com.heima.wemedia.service.WmNewsService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author TheCai
 * @date 2021/5/28 21:17
 */
@Slf4j
@Service
public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper, WmNews> implements WmNewsService {

  @Value("${file.oss.web-site}")
  private String webSite;
  /**
   * 查询所有自媒体文章
   *
   * @param dto
   * @return
   */
  @Override
  public ResponseResult findAll(WmNewsPageReqDto dto) {
    // 1.检查参数
    dto.checkParam();
    // 用户是否登录
    WmUser user = WmThreadLocalUtils.getUser();
    if (user == null) {
      return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
    }
    // 2.封装查询条件
    LambdaQueryWrapper<WmNews> lambdaQuery = Wrappers.<WmNews>lambdaQuery();
    // 2.1分页条件
    IPage<WmNews> wmNewsPage = new Page<>(dto.getPage(),dto.getSize());
    // 2.2查看状态条件是否存在
    if (dto.getStatus() != null) {
      lambdaQuery.eq(WmNews::getStatus, dto.getStatus());
    }
    // 2.3查看关键字是否存在
    if (StringUtils.isNotBlank(dto.getKeyword())) {
      lambdaQuery.like(WmNews::getTitle, dto.getKeyword());
    }
    // 2.4查看频道ID是否存在
    if (dto.getChannelId() != null) {
      lambdaQuery.eq(WmNews::getChannelId, dto.getChannelId());
    }
    // 2.5发布日期  查看发布日期  和结束日期是否存在
    if (dto.getBeginPubdate() != null && dto.getEndPubdate() != null) {
      lambdaQuery.between(WmNews::getPublishTime, dto.getBeginPubdate(), dto.getEndPubdate());
    }
    // 2.6登录用户的id
    lambdaQuery.eq(WmNews::getUserId, user.getId());
    // 2.7按照发布时间降序
    lambdaQuery.orderByDesc(WmNews::getPublishTime);
    // 执行查询
    IPage<WmNews> pageReslut = this.page(wmNewsPage, lambdaQuery);
    List<WmNews> wmNewsList = pageReslut.getRecords();

    // 4.封装返回结果
    ResponseResult result =
        new PageResponseResult(dto.getPage(), dto.getSize(), pageReslut.getTotal());

    result.setData(wmNewsList);
    // 要把图片访问路径带上
    result.setHost(webSite); // 存储图片访问前缀地址

    return result;
  }

  /**
   * 发布文章
   *
   * @param dto
   * @return
   */
  @Override
  public ResponseResult submitNews(WmNewsDto dto) {
    // 1.检查参数  封装WmNews对象
    // 1.1检查文章内容
    if (dto == null || StringUtils.isBlank(dto.getContent())) {
      return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE, "文章内容不能为空");
    }

    // 1.2文件的状态   只能为0 草稿  或1 待审核
    Short isSubmit = dto.getStatus();
    if (isSubmit == null
        || !isSubmit.equals(WemediaConstants.WM_NEWS_DRAFT_STATUS)
            && !isSubmit.equals(WemediaConstants.WM_NEWS_SUMMIT_STATUS)) {
      return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE, "文章状态不对");
    }
    // 1.3是否登录
    WmUser user = WmThreadLocalUtils.getUser();
    if (user == null) {
      return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
    }
    // 1.4基于dto封装wmNews
    WmNews wmNews = new WmNews();

    BeanUtils.copyProperties(dto, wmNews);
    // 1.5如果type为自动生成 暂时将type设置为null
    if (dto.getType().equals(WemediaConstants.WM_NEWS_TYPE_AUTO)) {
      wmNews.setType(null); // 这个字段数据库中用的是无符号字段,无法存负数
    }
    // 1.6封面images集合不为null  拼接成字符串(用逗号连接) 并替换前缀
    List<String> imagesList = dto.getImages();
    if (imagesList != null && imagesList.size() > 0) {
      wmNews.setImages(imagesListToStr(imagesList));
    }
    // 2.保存或修改WmNews对象
    wmNews.setUserId(user.getId());
    saveWmNews(wmNews);
    // 3.保存文章和素材的关联关系
    // 3.1抽取文章内容中涉及的图片
    List<String> contentImageList = parseContentImages(dto.getContent());
    // 3.2保存文章内容所引用的图片素材的关联关系
    if (isSubmit.equals(WemediaConstants.WM_NEWS_SUMMIT_STATUS)
        && contentImageList.size() > 0
        && contentImageList != null) {
      saveRelativeInfoForContent(contentImageList, wmNews);
    }
    // 3.3保存 文章封面所引用的图片素材的关联关系
    if (isSubmit.equals(WemediaConstants.WM_NEWS_SUMMIT_STATUS)) {
      saveRelativeInfoForCover(contentImageList, wmNews, dto);

      kafkaTemplate.send(NewsAutoScanConstants.WM_NEWS_AUTO_SCAN_TOPIC, wmNews.getId().toString());
      log.info("发表文章成功,并通过kafka通知admin端审核  ====> {}", wmNews.getId());
    }

    // 如果type类型为-1  需要从文章内容图片中生成封面

    return ResponseResult.okResult();
  }

  @Autowired KafkaTemplate<String, String> kafkaTemplate;

  @Override
  public ResponseResult findWmNewsById(Integer id) {
    if (id == null) {
      throw new CustomException(AppHttpCodeEnum.PARAM_INVALID);
    }
    WmNews wmNews = getById(id);
    if (wmNews == null) {
      throw new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST);
    }
    ResponseResult result = ResponseResult.okResult(wmNews);
    result.setHost(webSite);
    return result;
  }

  @Transactional
  @Override
  public ResponseResult delNews(Integer id) {
    // 1.检查参数
    if (id == null) {
      throw new CustomException(AppHttpCodeEnum.PARAM_INVALID);
    }
    WmUser user = WmThreadLocalUtils.getUser();
    if (user == null) {
      throw new CustomException(AppHttpCodeEnum.NEED_LOGIN);
    }
    // 2.获取数据
    WmNews wmNews = getById(id);
    if (wmNews == null) {
      throw new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST);
    }
    // 3.判断当前文章的状态  status==9  enable == 1,已上架的文章不能删除,下架的可以删除
    if (wmNews.getStatus().equals(WemediaConstants.WM_NEWS_PUBLISH_STATUS)
        && wmNews.getEnable().equals(WemediaConstants.WM_NEWS_ENABLE_UP)) {
      throw new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST, "发布且上架的文章不能删除");
    }
    // 判断是否是登录用户上传的
    if (!wmNews.getUserId().equals(user.getId())) {
      throw new CustomException(AppHttpCodeEnum.NO_OPERATOR_AUTH, "不是你的,无权限操作");
    }

    // 删除文章数据
    removeById(id);
    // 4.去除素材与文章的关联数据
    wmNewsMaterialMapper.delete(
        Wrappers.<WmNewsMaterial>lambdaQuery().eq(WmNewsMaterial::getNewsId, id));
    // 5.删除文章
    return ResponseResult.okResult();
  }

  @Override
  public ResponseResult downOrUp(WmNewsDto dto) {
    // 1.检查参数(id    enable =0 或 1)
    if (dto.getId() == null) {
      throw new CustomException(AppHttpCodeEnum.PARAM_INVALID, "文章id不能为空");
    }
    if (!WemediaConstants.WM_NEWS_ENABLE_UP.equals(dto.getEnable())
        && !WemediaConstants.WM_NEWS_ENABLE_DOWN.equals(dto.getEnable())) {
      throw new CustomException(AppHttpCodeEnum.PARAM_INVALID, "文章上下状态错误");
    }

    WmUser user = WmThreadLocalUtils.getUser();
    if (user == null) {
      throw new CustomException(AppHttpCodeEnum.NEED_LOGIN);
    }
    // 2.查询文章
    WmNews wmNews = getById(dto.getId());
    if (wmNews == null) {
      throw new CustomException(AppHttpCodeEnum.PARAM_INVALID);
    }
    // 3.判断文章是否发布
    if (!wmNews.getStatus().equals(WmNews.Status.PUBLISHED.getCode())) {
      return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST, "当前文章不是发布状态，不能上下架");
    }

    // 4.修改文章状态，同步到app端（后期做）TODO
    LambdaUpdateWrapper<WmNews> lambdaUpdate = Wrappers.<WmNews>lambdaUpdate();
    lambdaUpdate.set(WmNews::getEnable, dto.getEnable());
    // 要改审核状态,如果是下架的话,审核状态得改为提交待审核
    if (dto.getEnable().equals(WemediaConstants.WM_NEWS_ENABLE_DOWN)) {
      lambdaUpdate.set(WmNews::getStatus, WemediaConstants.WM_NEWS_SUMMIT_STATUS);
    }
    lambdaUpdate.eq(WmNews::getId, dto.getId()).eq(WmNews::getUserId, user.getId());
    update(lambdaUpdate);

    // TODO 对artic端发送上下架消息,需要文章id和上下架情况
    if (wmNews.getArticleId()!=null){
      HashMap map = new HashMap<>();
      map.put("articleId",wmNews.getArticleId());
      map.put("enable",dto.getEnable());
      kafkaTemplate.send(WmNewsMessageConstants.WM_NEWS_UP_OR_DOWN_TOPIC, JSONUtil.toJsonStr(map));
      log.info("文章上下架消息,同步通知article消息,发送成功============================");


      // ==============新加代码==============================================
      map.put("articleId",wmNews.getArticleId());
      if(dto.getEnable().intValue() == 0){
        kafkaTemplate.send(ArticleForEsConstants.ARTICLE_ELASTICSEARCH_DELETE,JSONUtil.toJsonStr(map));
      }else {
        kafkaTemplate.send(ArticleForEsConstants.ARTICLE_ELASTICSEARCH_ADD,JSONUtil.toJsonStr(map));
      }
      log.info("文章上下架消息,同步通知搜索微服务,发送成功============================");
      // ==============新加代码==============================================
    }






    return ResponseResult.okResult();
  }

  /**
   * 查询需要发布的文章id列表
   *
   * @return
   */
  @Override
  public List<Integer> findRelease() {
    // 查询状态为4或为8的数据且发布时间小于当前时间
    List<WmNews> WmNewsList =
        this.list(
            Wrappers.<WmNews>lambdaQuery()
                .le(WmNews::getPublishTime, new Date())
                .in(WmNews::getStatus, 4, 8)
                // 因为只需要id就不要别的属性了
                .select(WmNews::getId));

    List<Integer> list = WmNewsList.stream().map(WmNews::getId).collect(Collectors.toList());

    return list;
  }

  @Autowired WmNewsMapper wmNewsMapper;

  @Override
  public ResponseResult findList(NewsAuthDto dto) {

    // 检查参数
    // 判断分页参数
    dto.checkParam();
    // 封装查询条件
    Integer curryPage = dto.getPage();
    dto.setPage((curryPage - 1) * dto.getSize());
    // 标题
    String title = dto.getTitle();
    // 判断标题
    if (StrUtil.isNotEmpty(title)) {
      // 拼接查询条件
      dto.setTitle("%" + title + "%");
    }
    // 调用mapper执行查询,这里要用两表查询 wm_news和wm_user
    List<WmNewsVo> listAndPage = wmNewsMapper.findListAndPage(dto);

    long count = wmNewsMapper.findListCount(dto);

    ResponseResult responseResult = new PageResponseResult(curryPage, dto.getSize(), count);
    responseResult.setData(listAndPage);

    responseResult.setHost(webSite);

    return responseResult;
  }

  @Override
  public ResponseResult findWmNEwsVo(Integer id) {
    // 检查参数
    if (id == null) {
      throw new CustomException(AppHttpCodeEnum.PARAM_INVALID);
    }
    // 调用mapper执行查询,这里要用两表查询 wm_news和wm_user
    WmNewsVo WmNewsVo = wmNewsMapper.findByWmId(id);
    // 返回
    ResponseResult responseResult = new ResponseResult();
    responseResult.setData(WmNewsVo);

    responseResult.setHost(webSite);

    return responseResult;
  }

  @Override
  public ResponseResult updateStatus(short status, NewsAuthDto dto) {
    // 检查参数
    if (dto == null || dto.getId() == null) {
      throw new CustomException(AppHttpCodeEnum.PARAM_INVALID);
    }
    WmNews wmNews = this.getById(dto.getId());
    if (wmNews == null) {
      throw new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST);
    }
    // 将状态修改掉
    wmNews.setStatus(status);

    if (StrUtil.isNotEmpty(dto.getMsg())) {
      wmNews.setReason(dto.getMsg());
    }
    // 修改
    this.updateById(wmNews);

    return ResponseResult.okResult();
  }

  /**
   * 保存封面图片与素材关联关系 如果封面是自动生成 需要按照规则 从内容图片中获取封面
   *
   * @param contentImageList 内容中图片列表
   * @param wmNews 要保存的文章信息
   * @param dto 入参实体类
   */
  private void saveRelativeInfoForCover(
      List<String> contentImageList, WmNews wmNews, WmNewsDto dto) {
    // 如果type类型为-1  需要从文章内容图片中生成封面
    // 先得到图片地址集合
    List<String> imageList = dto.getImages();
    int size = contentImageList.size();
    // 判断是否自动生成封面,也就是type是不是-1
    if (WemediaConstants.WM_NEWS_TYPE_AUTO.equals(dto.getType())) {
      // 先判断一下list是不是非空非零
      if (CollectionUtil.isNotEmpty(contentImageList)) {
        if (size > 2) {
          // 1.如果图片数量大于2  type=3 多图 取内存中前3张图片作为封面
          // 内存中前3张图片作为封面
          imageList = contentImageList.stream().limit(3).collect(Collectors.toList());
          // type=3
          wmNews.setType(WemediaConstants.WM_NEWS_MANY_IMAGE);
        } else if (size > 0 && size <= 2) {
          // 2.如果图片数量大于0  小于等于2  tye=1 单图 取内容中前一张图片作为封面
          imageList = contentImageList.stream().limit(1).collect(Collectors.toList());
          wmNews.setType(WemediaConstants.WM_NEWS_SINGLE_IMAGE);
        }
      } else {
        // 3.其他情况  无图模式  type =0
        wmNews.setType(WemediaConstants.WM_NEWS_NONE_IMAGE);
      }
      // 将封面图片集合 转为逗号拼接的字符串
      wmNews.setImages(imagesListToStr(imageList)); // 图片字符串
      // 修改一下wmNews
      this.updateById(wmNews);
    }

    if (CollectionUtil.isNotEmpty(imageList)) {
      // 确保替换掉路径前缀
      imageList =
          imageList.stream().map(url -> url.replaceAll(webSite, "")).collect(Collectors.toList());
      // 保存封面 与 素材的关联关系
      saveRelativeInfo(imageList, wmNews.getId(), WemediaConstants.WM_IMAGE_REFERENCE);
    }
  }

  /**
   * 保存内容中引用素材的关联关系
   *
   * @param contentImageList
   * @param wmNews
   */
  private void saveRelativeInfoForContent(List<String> contentImageList, WmNews wmNews) {

    saveRelativeInfo(contentImageList, wmNews.getId(), WemediaConstants.WM_CONTENT_REFERENCE);
  }

  @Autowired WmMaterialMapper wmMaterialMapper;
  /**
   * 保存文章和素材的关联关系
   *
   * @param urlList 素材的图片路径集合
   * @param newsId 文章的id
   * @param type 引用的类型 0是内容引用 1 是封面引用
   */
  private void saveRelativeInfo(List<String> urlList, Integer newsId, short type) {
    // 封装查询条件
    LambdaQueryWrapper<WmMaterial> lambdaQuery = Wrappers.<WmMaterial>lambdaQuery();
    lambdaQuery
        .eq(WmMaterial::getUserId, WmThreadLocalUtils.getUser().getId()) // 注意这里按userId查的!!!
        .in(WmMaterial::getUrl, urlList);
    // 根据素材路径集合 及 登录也会有userId 查询素材集合
    List<WmMaterial> materialList = wmMaterialMapper.selectList(lambdaQuery);

    if (CollectionUtil.isEmpty(materialList)) {
      throw new CustomException(AppHttpCodeEnum.PARAM_INVALID, "引用参数不存在");
    }
    // 将素材列表转为mao集合  key:图片url路径  value : 素材的id
    Map<String, Integer> urlAndId =
        materialList.stream().collect(Collectors.toMap(WmMaterial::getUrl, WmMaterial::getId));
    // 预备一个集合接收素材id
    List<Integer> materialId = new ArrayList<>();
    urlList.forEach(
        url -> {
          if (!urlAndId.containsKey(url)) {
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID, "引用的素材不存在");
          }
          // 添加素材id到集合
          materialId.add(urlAndId.get(url));
        });
    // 添加到数据库
    wmNewsMaterialMapper.saveRelations(materialId, newsId, type);
  }

  /**
   * 将文章内容中的图片抽取出来 转为List集合
   *
   * @param content
   * @return
   */
  private List<String> parseContentImages(String content) {
    List<Map> contentImages = JSONArray.parseArray(content, Map.class);
    return contentImages.stream()
        // 过滤 保留type=image的数据
        .filter(map -> WemediaConstants.WM_NEWS_TYPE_IMAGE.equals(map.get("type")))
        // 得到每一个value:图片路径
        .map(map -> (String) map.get("value"))
        // 路径去掉前缀
        .map(url -> url.replaceAll(webSite, ""))
        // 收集为一个集合
        .collect(Collectors.toList());
  }

  /**
   * 将图片列表集合,用逗号拼接成一个字符串,并且替换前缀路径
   *
   * @return
   */
  private String imagesListToStr(List<String> imagesList) {
    return imagesList.stream() // 获取数据流
        .map(url -> url.replaceAll(webSite, "")) // 将url路径的前缀去掉
        .collect(Collectors.joining(",")); // 将所有路径 用逗号拼接成字符串
  }

  @Autowired WmNewsMaterialMapper wmNewsMaterialMapper;
  /**
   * 保存或修改文章
   *
   * @param wmNews 文章对象（前端传递）
   */
  private void saveWmNews(WmNews wmNews) {
    // 1.补全wnNews信息  创建时间  提交时间 登录用户 上下架
    wmNews.setCreatedTime(new Date());
    wmNews.setSubmitedTime(new Date()); // 补全提交时间
    wmNews.setEnable(WemediaConstants.WM_NEWS_ENABLE_UP);
    // 2.判断id值是否为空
    // 2.1如果id值为空  新增该wnNews
    if (wmNews.getId() == null) {
      this.save(wmNews);
    } else {
      // 2.2如果id值不为空  先删除之前的素材与文章的关系  然后修改wmNews
      wmNewsMaterialMapper.delete(
          Wrappers.<WmNewsMaterial>lambdaQuery().eq(WmNewsMaterial::getNewsId, wmNews.getId()));
      updateById(wmNews); // 修改文章
    }
  }
}
